package yunjiao.javatutorials.guava.concurrent;

import com.google.common.util.concurrent.Striped;
import lombok.Getter;

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;

/**
 * 缓存池管理示例：使用 Guava Striped.readWriteLock
 *
 * @author yangyunjiao
 */
public class CachePoolManager {
    // 创建 Striped 读写锁，64个条纹
    private static final Striped<ReadWriteLock> stripedLocks = Striped.readWriteLock(64);

    // 缓存数据存储
    private final ConcurrentMap<String, CacheEntry> cache = new ConcurrentHashMap<>();

    // 缓存条目类
    private static class CacheEntry {
        @Getter
        private final Object data;
        private final long timestamp;
        private final long ttl; // 生存时间（毫秒）

        public CacheEntry(Object data, long ttl) {
            this.data = data;
            this.timestamp = System.currentTimeMillis();
            this.ttl = ttl;
        }

        public boolean isExpired() {
            return System.currentTimeMillis() - timestamp > ttl;
        }

    }

    /**
     * 从缓存获取数据
     */
    public Object get(String key) {
        ReadWriteLock lock = stripedLocks.get(key);
        lock.readLock().lock();
        try {
            CacheEntry entry = cache.get(key);
            if (entry != null && !entry.isExpired()) {
                System.out.println(Thread.currentThread().getName() + " 读取缓存: " + key + " = " + entry.getData());
                return entry.getData();
            }
            return null; // 缓存不存在或已过期
        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * 写入缓存数据
     */
    public void put(String key, Object value, long ttl) {
        ReadWriteLock lock = stripedLocks.get(key);
        lock.writeLock().lock();
        try {
            CacheEntry entry = new CacheEntry(value, ttl);
            cache.put(key, entry);
            System.out.println(Thread.currentThread().getName() + " 写入缓存: " + key + " = " + value + " (TTL: " + ttl + "ms)");
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 删除缓存数据
     */
    public void remove(String key) {
        ReadWriteLock lock = stripedLocks.get(key);
        lock.writeLock().lock();
        try {
            cache.remove(key);
            System.out.println(Thread.currentThread().getName() + " 删除缓存: " + key);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 获取或计算缓存（经典缓存模式）
     */
    public Object getOrCompute(String key, CacheLoader loader, long ttl) {
        // 首先尝试读锁
        Object value = get(key);
        if (value != null) {
            return value;
        }

        // 缓存未命中，获取写锁
        ReadWriteLock lock = stripedLocks.get(key);
        lock.writeLock().lock();
        try {
            // 双重检查，防止其他线程已经计算了值
            CacheEntry entry = cache.get(key);
            if (entry != null && !entry.isExpired()) {
                return entry.getData();
            }

            // 计算新值
            value = loader.load(key);
            if (value != null) {
                put(key, value, ttl);
            }
            return value;
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 清空所有缓存
     */
    public void clear() {
        cache.clear();
    }

    /**
     * 缓存加载器接口
     */
    public interface CacheLoader {
        Object load(String key);
    }

    // 测试代码
    public static void main(String[] args) throws InterruptedException {
        CachePoolManager cacheManager = new CachePoolManager();
        Random random = new Random();

        // 模拟多个线程并发访问缓存
        int threadCount = 10;
        Thread[] threads = new Thread[threadCount];

        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            threads[i] = new Thread(() -> {
                String[] keys = {"user:1", "user:2", "product:A", "product:B"};

                for (int j = 0; j < 5; j++) {
                    String key = keys[random.nextInt(keys.length)];

                    // 随机选择操作：读、写、或获取或计算
                    int operation = random.nextInt(3);
                    switch (operation) {
                        case 0: // 读操作
                            cacheManager.get(key);
                            break;
                        case 1: // 写操作
                            cacheManager.put(key, "Value_from_thread_" + threadId, 5000);
                            break;
                        case 2: // 获取或计算操作
                            cacheManager.getOrCompute(key,
                                    k -> "Computed_value_for_" + k + "_by_thread_" + threadId,
                                    3000);
                            break;
                    }

                    try {
                        TimeUnit.MILLISECONDS.sleep(random.nextInt(100));
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            });
        }

        // 启动所有线程
        for (Thread thread : threads) {
            thread.start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("\n=== 最终缓存状态 ===");
        cacheManager.clear();
    }
}
